﻿namespace CloudNinja.Metering
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;

    using CloudNinja.Metering.BlobLeaseUtil;
    using CloudNinja.Metering.Common.Configuration;
    using CloudNinja.Metering.Common.Logging;
    using CloudNinja.Metering.Data;
    using CloudNinja.Metering.Providers;
    using CloudNinja.Metering.TypeRegistration;

    using Microsoft.Practices.Unity;

    public class Metering : IDisposable
    {
        private readonly ITickTock tickTock;

        private readonly ILicenseToRun executionLicense;

        private readonly ILogger logger;

        private readonly List<IMeteringProvider> providers;

        private readonly IUnityContainer typeContainer;

        private readonly IMeteringRepository meteringRepository;

        public Metering(ILicenseToRun executionLicense, ILogger logger, ITickTock tickTock)
        {
            this.executionLicense = executionLicense;
            this.logger = logger;
            this.tickTock = tickTock;
            this.providers = new List<IMeteringProvider>();
            this.meteringRepository = meteringRepository;
            this.typeContainer = MeteringContainer.Current.GetInstance<IUnityContainer>();
        }

        public void Start()
        {
            this.Initialize();
            this.tickTock.Start();
        }

        public void Stop()
        {
            foreach (var meteringProvider in this.providers)
            {
                meteringProvider.Stop();
            }

            this.Dispose();
        }

        public void Dispose()
        {
            this.tickTock.Dispose();
            this.executionLicense.Dispose();
        }

        private void RegisterConfiguredProviders()
        {
            var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            var meteringSection = config.Sections["MeteringConfiguration"] as MeteringSection;

            if (meteringSection == null)
            {
                throw new ConfigurationErrorsException("Cannot find MeteringConfiguration section in the .config file.");
            }

            if (meteringSection.ProviderSettings.Providers != null)
            {
                var providersCollection = meteringSection.ProviderSettings.Providers;

                foreach (var typeRegistrar in providersCollection.OfType<ITypeRegistration>())
                {
                    typeRegistrar.RegisterType(this.typeContainer, meteringSection);
                    this.logger.LogInformation(string.Format("Registered provider {0}", typeRegistrar.GetType().Name));
                }
            }
        }

        private void Initialize()
        {
            this.RegisterConfiguredProviders();

            var configuredProviders = this.typeContainer.ResolveAll<IMeteringProvider>();

            foreach (var provider in configuredProviders)
            {
                this.AddProvider(provider);
            }

            foreach (var meteringProvider in this.providers)
            {
                meteringProvider.Initialize();
                this.tickTock.RegisterWork(meteringProvider.GetType().Name, meteringProvider.Collect, meteringProvider.CollectInterval);
            }
        }

        private void AddProvider(IMeteringProvider provider)
        {
            this.providers.Add(provider);
        }
    }
}
